package com.xiaoshuidi.cloud.module.report.service.ajreport;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.xiaoshuidi.cloud.framework.common.pojo.PageResult;
import com.xiaoshuidi.cloud.framework.security.core.LoginUser;
import com.xiaoshuidi.cloud.module.contract.enums.contract.ContractNodeEnum;
import com.xiaoshuidi.cloud.module.contract.enums.contract.ContractStatusEnum;
import com.xiaoshuidi.cloud.module.report.constant.FlowTypeEnum;
import com.xiaoshuidi.cloud.module.report.controller.finance.vo.ContractRoomDepositReqVO;
import com.xiaoshuidi.cloud.module.report.controller.finance.vo.ContractRoomDepositRespVO;
import com.xiaoshuidi.cloud.module.report.convert.finance.ReportContractRoomDepositConvert;
import com.xiaoshuidi.cloud.module.report.dal.DO.ContractRoomDO;
import com.xiaoshuidi.cloud.module.report.dal.DO.report.ReportContractRoomDepositDO;
import com.xiaoshuidi.cloud.module.report.dal.mapper.ContractRoomMapper;
import com.xiaoshuidi.cloud.module.report.dal.mapper.ContractSettlementDetailMapper;
import com.xiaoshuidi.cloud.module.report.dal.mapper.ContractSettlementMapper;
import com.xiaoshuidi.cloud.module.report.dal.mapper.report.ReportContractRoomDepositMapper;
import com.xiaoshuidi.cloud.module.report.service.ajreport.dto.ContractSellementVo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

import static com.xiaoshuidi.cloud.framework.security.core.util.SecurityFrameworkUtils.getLoginUser;

@Service
@RequiredArgsConstructor
@Slf4j
public class ReportContractRoomDepositServiceImpl implements ReportContractRoomDepositService {

    private final ContractRoomMapper contractRoomMapper;
    private final ReportContractRoomDepositMapper reportContractRoomDepositMapper;
    private final ContractSettlementMapper contractSettlementMapper;
    private final ContractSettlementDetailMapper contractSettlementDetailMapper;

    @Override
    public void insertBathByTime(LocalDateTime startTime, LocalDateTime endTime) {
        reportContractRoomDepositMapper.insertBathByTime(startTime, endTime);
    }

    /**
     * 数据处理 未结束的数据
     */
    @Override
    public void dataProcessing() {
        Page<ReportContractRoomDepositDO> page = new Page<>(1, 1000);
        LambdaQueryWrapper<ReportContractRoomDepositDO> queryWrapper =
                new LambdaQueryWrapper<ReportContractRoomDepositDO>()
                        .eq(ReportContractRoomDepositDO::getHandleFlag, 0).orderByAsc(ReportContractRoomDepositDO::getId);
        report(page, queryWrapper);
    }

    private void report(Page<ReportContractRoomDepositDO> page, Wrapper<ReportContractRoomDepositDO> queryWrapper) {
        log.info("第===================={}================================页", page.getCurrent());
        Page<ReportContractRoomDepositDO> pageList =
                reportContractRoomDepositMapper.selectPage(page, queryWrapper);
        List<ReportContractRoomDepositDO> records = pageList.getRecords();
        if (CollectionUtil.isNotEmpty(records)) {
            List<Long> collect1 =
                    records.stream().map(ReportContractRoomDepositDO::getContractId).collect(Collectors.toList());
            //数据处理
            List<ContractRoomDO> contractRoomDOS =
                    contractRoomMapper.selectBatchIds(collect1);
            //续租 或者换租新合同
            List<ContractRoomDO> contractRoomFormDos = contractRoomMapper.selectList(new QueryWrapper<ContractRoomDO>()
                    .in("from_id",collect1)
                    .in("contract_status",ContractStatusEnum.CHECK_IN.getCode()
                            ,ContractStatusEnum.FREEZE.getCode()
                            ,ContractStatusEnum.ACTIVE.getCode()
                            ,ContractStatusEnum.NORMAL_END.getCode()
                            ,ContractStatusEnum.ABNORMAL_END.getCode()
                    )
                    .eq("deleted",0));


            List<ReportContractRoomDepositDO> removeList = new ArrayList<>();
            //更新数据
            List<ReportContractRoomDepositDO> updateList = new ArrayList<>();
            //获取清算单
            List<ContractSellementVo> contractSellementVos =
                    contractSettlementDetailMapper.selectDepositByContractIds(collect1);
            //未找到合同数据这批数据直接标记删除 无需处理
            records.forEach(f -> {
                if (CollectionUtil.isEmpty(contractRoomDOS)) {
                    removeList.add(f);
                } else {
                    Optional<ContractRoomDO> first =
                            contractRoomDOS.stream().filter(r -> r.getId().compareTo(f.getContractId()) == 0).findFirst();
                    //匹配合同
                    if (first.isPresent()) {
                        ContractRoomDO contractRoomDO = first.get();
                        //合同状态有变更进行更新
                        ReportContractRoomDepositDO depositDO = new ReportContractRoomDepositDO();
                        depositDO.setId(f.getId());
                        depositDO.setContractStatus(contractRoomDO.getContractStatus());
                        depositDO.setContractNode(contractRoomDO.getContractNode());
                        if ((ContractNodeEnum.RENTING_OUT.getCode().equals(contractRoomDO.getContractNode()) ||
                                ContractNodeEnum.RENEWAL.getCode().equals(contractRoomDO.getContractNode()))
                                && (ContractStatusEnum.NORMAL_END.getCode().equals(contractRoomDO.getContractStatus()) ||
                                ContractStatusEnum.ABNORMAL_END.getCode().equals(contractRoomDO.getContractStatus()))) {
                            //获取当前合同的清算单
                            List<ContractSellementVo> contractSellementVoStream =
                                    contractSellementVos.stream().filter(sfb -> f.getContractId().compareTo(sfb.getContractId()) == 0).collect(Collectors.toList());
                            //查到清算单
                            if (CollectionUtil.isNotEmpty(contractSellementVoStream)) {
                                List<ContractSellementVo> collect =
                                        contractSellementVoStream.stream().filter(t -> StrUtil.isNotEmpty(t.getFlowType())
                                                &&ObjectUtil.isNotEmpty(t.getRefundableAmount())).collect(Collectors.toList());
                                //有值
                                if (CollectionUtil.isNotEmpty(collect)) {
                                    //存在退租单 但是无押金流流出项目
                                    depositDO.setReturnAmount(collect.stream().peek(pk->{
                                                //流入情况下
                                                if(FlowTypeEnum.IN.getCode().equals(pk.getFlowType()) && pk.getRefundableAmount().compareTo(BigDecimal.ZERO)!=0){
                                                    //取反
                                                    pk.setRefundableAmount(pk.getRefundableAmount().negate());
                                                }
                                            }).map(ContractSellementVo::getRefundableAmount)
                                            .reduce(BigDecimal.ZERO, BigDecimal::add));
                                    if(CollectionUtil.isNotEmpty(contractRoomFormDos)){
                                        //获取下一份合同
                                        contractRoomFormDos.stream().filter(ff->ff.getFromId().compareTo(contractRoomDO.getId())==0).findFirst().ifPresent(
                                                ffp->{
                                                    //不是一个房源就是换房
                                                    if(ffp.getRoomId()!=null && contractRoomDO.getRoomId()!=null && ffp.getRoomId().compareTo(contractRoomDO.getRoomId())!=0
                                                       && ffp.getDeposit()!=null){
                                                        depositDO.setReturnAmount(depositDO.getReturnAmount().add(ffp.getDeposit()));
                                                    }
                                                }
                                        );
                                    }
                                } else {
                                    //存在退租单 但是无押金流流出项目
                                    depositDO.setReturnAmount(BigDecimal.ZERO);
                                }
                            } else {
                                //默认退全部
                                depositDO.setReturnAmount(f.getDeposit());
                            }
                            depositDO.setHandleFlag(1);

                        }
                        updateList.add(depositDO);
                    }
//                    else {
//                        removeList.add(f);
//                    }
                }
            });
            //被删除数据报表移除
//            if(CollectionUtil.isNotEmpty(removeList)){
//                reportContractRoomDepositMapper
//                        .deleteBatchIds(removeList.stream().map(ReportContractRoomDepositDO::getId).collect
//                        (Collectors.toList()));
//            }
            //更新数据
            if (CollectionUtil.isNotEmpty(updateList)) {
                reportContractRoomDepositMapper
                        .updateBatch(updateList, updateList.size());
            }
            //数据处理结束
            //递归处理
            if (records.size() == page.getSize()) {
                page.setCurrent(page.getCurrent() + 1);
                report(page, queryWrapper);
            }

        }
    }

    @Override
    public void dataProcessingByTime(LocalDateTime startTime, LocalDateTime endTime) {
        //修改时间获取每日增量数据
        Page<ReportContractRoomDepositDO> page = new Page<>(1, 1000);
        reportPage(page, startTime, endTime);

    }

    private void reportPage(Page<ReportContractRoomDepositDO> page, LocalDateTime startTime, LocalDateTime endTime) {
        log.info("第===================={}================================页", page.getCurrent());
        Page<ReportContractRoomDepositDO> pageList =
                reportContractRoomDepositMapper.selectPageListByTime(page, startTime, endTime);
        List<ReportContractRoomDepositDO> records = pageList.getRecords();
        if (CollectionUtil.isNotEmpty(records)) {
            List<Long> contractIds =
                    records.stream().map(ReportContractRoomDepositDO::getContractId).collect(Collectors.toList());
            //数据处理
            //获取清算单
            List<ContractSellementVo> contractSellementVos =
                    contractSettlementDetailMapper.selectDepositByContractIds(contractIds);
            //续租 或者换租新合同
            List<ContractRoomDO> contractRoomFormDos = contractRoomMapper.selectList(new QueryWrapper<ContractRoomDO>()
                    .in("from_id",contractIds)
                    .in("contract_status",ContractStatusEnum.CHECK_IN.getCode()
                            ,ContractStatusEnum.FREEZE.getCode()
                            ,ContractStatusEnum.ACTIVE.getCode()
                            ,ContractStatusEnum.NORMAL_END.getCode()
                            ,ContractStatusEnum.ABNORMAL_END.getCode()
                    )
                    .eq("deleted",0));


            records.stream().forEach(f -> {
                if ((ContractNodeEnum.RENTING_OUT.getCode().equals(f.getContractNode()) ||
                        ContractNodeEnum.RENEWAL.getCode().equals(f.getContractNode()))
                        && (ContractStatusEnum.NORMAL_END.getCode().equals(f.getContractStatus()) ||
                        ContractStatusEnum.ABNORMAL_END.getCode().equals(f.getContractStatus()))) {
                    //获取当前合同的清算单
                    List<ContractSellementVo> contractSellementVoStream =
                            contractSellementVos.stream().filter(sfb -> f.getContractId().compareTo(sfb.getContractId()) == 0).collect(Collectors.toList());
                    //查到清算单
                    if (CollectionUtil.isNotEmpty(contractSellementVoStream)) {
                        List<ContractSellementVo> collect =
                                contractSellementVoStream.stream().filter(t -> StrUtil.isNotEmpty(t.getFlowType())
                                        && ObjectUtil.isNotEmpty(t.getRefundableAmount())).collect(Collectors.toList());
                        //有值
                        if (CollectionUtil.isNotEmpty(collect)) {
                            //存在退租单 但是无押金流流出项目
                            f.setReturnAmount(collect.stream().peek(pk->{
                                        //流入情况下
                                        if(FlowTypeEnum.IN.getCode().equals(pk.getFlowType()) && pk.getRefundableAmount().compareTo(BigDecimal.ZERO)!=0){
                                            //取反
                                            pk.setRefundableAmount(pk.getRefundableAmount().negate());
                                        }
                                    }).map(ContractSellementVo::getRefundableAmount)
                                    .reduce(BigDecimal.ZERO, BigDecimal::add));
                            if(CollectionUtil.isNotEmpty(contractRoomFormDos)){
                                //获取下一份合同
                                contractRoomFormDos.stream().filter(ff->ff.getFromId().compareTo(f.getId())==0).findFirst().ifPresent(
                                        ffp->{
                                            //不是一个房源就是换房
                                            if(ffp.getRoomId()!=null && f.getRoomId()!=null && ffp.getRoomId().compareTo(f.getRoomId())!=0
                                                    && ffp.getDeposit()!=null){
                                                f.setReturnAmount(f.getReturnAmount().add(ffp.getDeposit()));
                                            }
                                        }
                                );
                            }
                        } else {
                            //存在退租单 但是无押金流流出项目
                            f.setReturnAmount(BigDecimal.ZERO);
                        }
                    } else {
                        //默认退全部
                        f.setReturnAmount(f.getDeposit());
                    }
                    f.setHandleFlag(1);
                }
            });
            //查询现有入库
            LambdaQueryWrapper<ReportContractRoomDepositDO> eq =
                    new LambdaQueryWrapper<ReportContractRoomDepositDO>().in(ReportContractRoomDepositDO::getContractId, contractIds);
            //已入库数据匹配
            List<ReportContractRoomDepositDO> reportContractRoomDepositDOS =
                    reportContractRoomDepositMapper.selectList(eq);
            //已入库id集合
            List<Long> oldContractIds =
                    reportContractRoomDepositDOS.stream().map(ReportContractRoomDepositDO::getContractId).collect(Collectors.toList());
            //新增
            List<ReportContractRoomDepositDO> addList =
                    records.stream().filter(f -> CollectionUtil.isEmpty(oldContractIds)
                                    || oldContractIds.stream().filter(ol->ol.compareTo(f.getContractId())==0).count() == 0)
                            .collect(Collectors.toList());
            if (CollectionUtil.isNotEmpty(addList)) {
                log.info("新增{}=============", addList.size());
                reportContractRoomDepositMapper.insertBatch(addList);
            }
            //修改
            List<ReportContractRoomDepositDO> updateList =
                    records.stream().filter(f -> CollectionUtil.isNotEmpty(oldContractIds)
                                    &&  oldContractIds.stream().filter(ol->ol.compareTo(f.getContractId())==0).count() > 0)
                            .peek(p -> {
                                reportContractRoomDepositDOS.stream().filter(fb -> fb.getContractId().compareTo(p.getContractId())==0).findFirst().ifPresent(ip -> {
                                    p.setId(ip.getId());
                                });
                            })
                            .collect(Collectors.toList());
            if (CollectionUtil.isNotEmpty(updateList)) {
                log.info("修改{}=============", updateList.size());
                reportContractRoomDepositMapper.updateBatch(updateList, updateList.size());
            }

            //数据处理结束
            //递归处理
            if (records.size() == page.getSize()) {
                page.setCurrent(page.getCurrent() + 1);
                reportPage(page, startTime, endTime);
            }

        }
    }

    /**
     * 分页
     *
     * @param reqVO
     * @return
     */
    @Override
    public ContractRoomDepositRespVO page(ContractRoomDepositReqVO reqVO) {
        LoginUser loginUser = getLoginUser();
        if (ObjectUtil.isNotNull(loginUser)) {
            reqVO.setTenantId(loginUser.getTenantId());
//            reqVO.setTenantId(10000L);
        }
        ContractRoomDepositRespVO vo = new ContractRoomDepositRespVO();
        vo.setPageResult(ReportContractRoomDepositConvert.INSTANCE.convertPageResult(pageList(reqVO)));
        QueryWrapper<ReportContractRoomDepositDO> queryWrapper = new QueryWrapper<ReportContractRoomDepositDO>()
                .select("\tSUM( deposit ) amountIn,\n" +
                        "\tSUM( return_amount ) amountOut,\n" +
                        "\tSUM( deposit ) - SUM( return_amount ) amountReduce")
                .ge(ObjectUtil.isNotNull(reqVO.getStart()), "actual_pay_time", reqVO.getStart())
                .le(ObjectUtil.isNotNull(reqVO.getEnd()), "actual_pay_time", reqVO.getEnd())
                .eq(ObjectUtil.isNotNull(reqVO.getTenantId()), "tenant_id", reqVO.getTenantId())
                .in(CollectionUtil.isNotEmpty(reqVO.getApartmentIds()), "apartment_id", reqVO.getApartmentIds())
                .eq(StrUtil.isNotEmpty(reqVO.getPayMethod()), "pay_method", reqVO.getPayMethod())
                .eq(StrUtil.isNotEmpty(reqVO.getContractType()), "customer_type", reqVO.getContractType())
                .and(StrUtil.isNotEmpty(reqVO.getMixQueryParam())
                        , q -> q.like("payer", reqVO.getMixQueryParam())
                                .or()
                                .like("room_name", reqVO.getMixQueryParam()));
        List<Map<String, Object>> maps = reportContractRoomDepositMapper.selectMaps(queryWrapper);
        if (CollectionUtil.isNotEmpty(maps) && ObjectUtil.isNotNull(maps.get(0))) {
            vo.setAmountIn(ObjectUtil.isNotNull(maps.get(0).get("amountIn")) ? new BigDecimal(maps.get(0).get(
                    "amountIn").toString()) : BigDecimal.ZERO);
            vo.setAmountOut(ObjectUtil.isNotNull(maps.get(0).get("amountOut")) ? new BigDecimal(maps.get(0).get(
                    "amountOut").toString()) : BigDecimal.ZERO);
            vo.setAmountReduce(ObjectUtil.isNotNull(maps.get(0).get("amountReduce")) ?
                    new BigDecimal(maps.get(0).get("amountReduce").toString()) : BigDecimal.ZERO);
        }
        return vo;
    }

    @Override
    public PageResult<ReportContractRoomDepositDO> pageList(ContractRoomDepositReqVO reqVO) {
        LambdaQueryWrapper<ReportContractRoomDepositDO> queryWrapper =
                new LambdaQueryWrapper<ReportContractRoomDepositDO>()
                        .ge(ObjectUtil.isNotNull(reqVO.getStart()), ReportContractRoomDepositDO::getActualPayTime,
                                reqVO.getStart())
                        .le(ObjectUtil.isNotNull(reqVO.getEnd()), ReportContractRoomDepositDO::getActualPayTime,
                                reqVO.getEnd())
                        .eq(ObjectUtil.isNotNull(reqVO.getTenantId()), ReportContractRoomDepositDO::getTenantId,
                                reqVO.getTenantId())
                        .in(CollectionUtil.isNotEmpty(reqVO.getApartmentIds()),
                                ReportContractRoomDepositDO::getApartmentId,
                                reqVO.getApartmentIds())
                        .eq(StrUtil.isNotEmpty(reqVO.getPayMethod()), ReportContractRoomDepositDO::getPayMethod,
                                reqVO.getPayMethod())
                        .eq(StrUtil.isNotEmpty(reqVO.getContractType()), ReportContractRoomDepositDO::getCustomerType,
                                reqVO.getContractType())
                        .and(StrUtil.isNotEmpty(reqVO.getMixQueryParam())
                                , q -> q.like(ReportContractRoomDepositDO::getPayer, reqVO.getMixQueryParam())
                                        .or()
                                        .like(ReportContractRoomDepositDO::getRoomName, reqVO.getMixQueryParam()))
                        .orderByDesc(ReportContractRoomDepositDO::getCreateTime, ReportContractRoomDepositDO::getId);
        PageResult<ReportContractRoomDepositDO> reportContractRoomDepositDOPageResult =
                reportContractRoomDepositMapper.selectPage(reqVO, queryWrapper);
        return reportContractRoomDepositDOPageResult;
    }
}
